function test_svm_stability()
% 测试SVM训练稳定性的验证脚本
% 通过多次训练来验证结果的一致性

fprintf('===== SVM训练稳定性测试 =====\n');
fprintf('此脚本将进行多次训练，验证结果的一致性\n\n');

% 配置测试参数
numTests = 3;  % 测试次数
trainFolder = 'D:\文档资料\OneDrive\桌面\人脸配准(眉毛特征点)\train';

% 检查训练文件夹
if ~exist(trainFolder, 'dir')
    fprintf('训练文件夹不存在: %s\n', trainFolder);
    fprintf('请修改 trainFolder 变量为您的实际训练数据路径\n');
    return;
end

fprintf('测试配置:\n');
fprintf('- 训练文件夹: %s\n', trainFolder);
fprintf('- 测试次数: %d\n', numTests);
fprintf('- 每次测试使用相同的固定随机种子\n\n');

% 存储测试结果
results = struct();
results.accuracies = [];
results.parameters = [];
results.timing = [];

% 进行多次训练测试
for testRun = 1:numTests
    fprintf('【第 %d/%d 次测试】\n', testRun, numTests);
    
    try
        % 记录开始时间
        startTime = tic;
        
        % 固定随机种子（确保每次测试使用相同的种子）
        rng(42, 'twister');
        
        % 简化的训练过程
        [accuracy, params] = performSimpleStabilityTest(trainFolder);
        
        % 记录结果
        elapsedTime = toc(startTime);
        results.accuracies(end+1) = accuracy;
        results.parameters{end+1} = params;
        results.timing(end+1) = elapsedTime;
        
        fprintf('✓ 测试完成 - 准确率: %.2f%%, 耗时: %.2f秒\n', accuracy, elapsedTime);
        
    catch ME
        fprintf('✗ 测试失败: %s\n', ME.message);
        continue;
    end
    
    fprintf('\n');
end

% 分析结果
fprintf('===== 稳定性测试结果分析 =====\n');

if length(results.accuracies) >= 2
    meanAccuracy = mean(results.accuracies);
    stdAccuracy = std(results.accuracies);
    minAccuracy = min(results.accuracies);
    maxAccuracy = max(results.accuracies);
    
    fprintf('准确率统计:\n');
    fprintf('- 平均值: %.2f%%\n', meanAccuracy);
    fprintf('- 标准差: %.4f%%\n', stdAccuracy);
    fprintf('- 最小值: %.2f%%\n', minAccuracy);
    fprintf('- 最大值: %.2f%%\n', maxAccuracy);
    fprintf('- 变异范围: %.4f%%\n', maxAccuracy - minAccuracy);
    
    % 稳定性评估
    if stdAccuracy < 0.1
        fprintf('\n🎉 优秀！SVM训练非常稳定（标准差 < 0.1%%）\n');
    elseif stdAccuracy < 1.0
        fprintf('\n✅ 良好！SVM训练基本稳定（标准差 < 1.0%%）\n');
    elseif stdAccuracy < 5.0
        fprintf('\n⚠️  一般！SVM训练稳定性中等（标准差 < 5.0%%）\n');
    else
        fprintf('\n❌ 不佳！SVM训练不稳定（标准差 ≥ 5.0%%）\n');
        fprintf('   建议检查数据质量和参数设置\n');
    end
    
    % 显示每次测试的详细结果
    fprintf('\n详细结果:\n');
    fprintf('测试轮次  准确率    耗时(秒)\n');
    fprintf('--------  --------  --------\n');
    for i = 1:length(results.accuracies)
        fprintf('%8d  %8.2f  %8.2f\n', i, results.accuracies(i), results.timing(i));
    end
    
else
    fprintf('测试数据不足，无法进行稳定性分析\n');
end

fprintf('\n===== 建议 =====\n');
fprintf('1. 如果标准差 < 0.1%%，说明训练非常稳定\n');
fprintf('2. 如果标准差 > 1.0%%，建议使用 stable_svm_training.m\n');
fprintf('3. 确保每次训练使用相同的随机种子\n');
fprintf('4. 使用固定的SVM参数而不是自动调优\n');

fprintf('\n===== 稳定性测试完成 =====\n');

end

function [accuracy, params] = performSimpleStabilityTest(trainFolder)
% 执行简化的稳定性测试

% 配置参数
config = struct();
config.trainImgPerPerson = 5;
config.pcaRatio = 0.95;
config.imgSize = 100;

% 固定SVM参数（避免自动调优的随机性）
svmParams = struct();
svmParams.kernelScale = 1.0;
svmParams.boxConstraint = 10;

% 1. 加载训练数据
[trainFaces, trainLabels] = loadTrainingDataForTest(trainFolder, config);

if isempty(trainFaces)
    error('未能加载训练数据');
end

% 2. 预处理
meanFace = mean(trainFaces, 2);
trainFaces = trainFaces - repmat(meanFace, 1, size(trainFaces, 2));

% 3. PCA降维
C = trainFaces' * trainFaces;
[V, D] = eig(C);
eigenValues = diag(D);
[eigenValues, sortIdx] = sort(eigenValues, 'descend');
V = V(:, sortIdx);

cumSum = cumsum(eigenValues) / sum(eigenValues);
numComponents = find(cumSum >= config.pcaRatio, 1);
selectedV = V(:, 1:numComponents);
eigenFaces = trainFaces * selectedV;
weightedTrain = eigenFaces' * trainFaces;

% 4. 训练SVM
X_train = weightedTrain';
Y_train = trainLabels;

svmModel = fitcecoc(X_train, Y_train, ...
    'Learners', templateSVM('KernelFunction', 'rbf', ...
                           'KernelScale', svmParams.kernelScale, ...
                           'BoxConstraint', svmParams.boxConstraint, ...
                           'Standardize', true), ...
    'Verbose', 0);

% 5. 测试准确率
predictions = predict(svmModel, X_train);
accuracy = mean(predictions == Y_train) * 100;

% 返回参数信息
params = svmParams;
params.numComponents = numComponents;
params.numSamples = length(Y_train);
params.numPersons = length(unique(Y_train));

end

function [trainFaces, trainLabels] = loadTrainingDataForTest(trainFolder, config)
% 为测试加载训练数据

trainFaces = [];
trainLabels = [];

% 获取所有图像文件
imgExtensions = {'.jpg', '.png', '.bmp', '.jpeg', '.tif'};
allFiles = [];

for i = 1:length(imgExtensions)
    pattern = ['*' imgExtensions{i}];
    files = dir(fullfile(trainFolder, pattern));
    allFiles = [allFiles; files];
end

if isempty(allFiles)
    return;
end

% 按文件名排序
[~, sortIdx] = sort({allFiles.name});
allFiles = allFiles(sortIdx);

% 解析文件名并分组
personData = containers.Map('KeyType', 'int32', 'ValueType', 'any');

for i = 1:length(allFiles)
    filename = allFiles(i).name;
    tokens = regexp(filename, '(\d+)[-_](\d+)', 'tokens');
    
    if ~isempty(tokens)
        personID = str2double(tokens{1}{1});
        imageSeq = str2double(tokens{1}{2});
        
        if isKey(personData, personID)
            data = personData(personID);
            data.sequences(end+1) = imageSeq;
            data.files(end+1) = i;
            personData(personID) = data;
        else
            data = struct();
            data.sequences = imageSeq;
            data.files = i;
            personData(personID) = data;
        end
    end
end

% 选择训练图像
personIDs = sort(cell2mat(keys(personData)));

for i = 1:length(personIDs)
    personID = personIDs(i);
    data = personData(personID);
    
    % 按序号排序
    [~, sortOrder] = sort(data.sequences);
    sortedFiles = data.files(sortOrder);
    
    % 选择前N张
    numToUse = min(config.trainImgPerPerson, length(sortedFiles));
    
    for j = 1:numToUse
        fileIdx = sortedFiles(j);
        imgPath = fullfile(trainFolder, allFiles(fileIdx).name);
        
        img = imread(imgPath);
        if size(img, 3) == 3
            img = rgb2gray(img);
        end
        
        img = imresize(img, [config.imgSize, config.imgSize]);
        imgVector = double(img(:));
        
        trainFaces = [trainFaces, imgVector];
        trainLabels = [trainLabels; personID];
    end
end

end 